package com.cskaoyan.javase.string._4api;

/**
 * @description: String的比较功能
 * @author: wuguidong@cskaoyan.onaliyun.com
 **/

import jdk.nashorn.internal.ir.annotations.Ignore;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;

/**
 * String的比较功能有两个:
 *      1.equals和equalsIgnoreCase 这两个方法专注于比较两个String对象是否相等,但是没有大小的概念
 *      2.compareTo和compareToIgnoreCase 这两个方法专注于比较两个String对象的大小,会区分谁大谁小
 *
 * compareTo的具体实现:
 *      字符串对象的大小怎么界定呢?需要查看compareTo方法具体实现,从而找出比较的规则
 *      总结:
 *          该方法会从头逐个比较字符串当中的每个字符,如果出现第一个不同字符,该方法返回它们的编码值之差
 *          而如果比较完较短字符串数组长度的次数后,还没有出现不同的元素,那就返回它们value数组的长度之差
 *          特殊的,当两个字符串内容完全一样,这时方法就返回长度之差 = 0
 *              举例: "abc"和"bc"比较,方法返回值是-1
 *              "bc"和"ab"比较,方法返回值是1
 *              "abc"和"ab"比较,方法返回值是1
 *              "a"和"abcde"比较,方法返回值是-4
 *              "abc"和"abc"比较,方法返回值是0
 *  类比Double.compare(d1,d2)方法:
 *      1.当compareTo方法返回小于0的数,意味着方法的调用者比方法传入参数的字符串要小
 *      2.当compareTo方法返回0,意味着方法的调用者比方法传入参数的字符串要相等
 *      3.当compareTo方法返回大于0的数,意味着方法的调用者比方法传入参数的字符串要大
 * 对字符串(对象)比较大小,就可以对它进行排序
 *      一个类实现了Comparable接口，实现了其中compareTo方法,就可以对这个类的对象（容器或集合）进行排序
 *      称之为自然排序，其中的compareTo方法被称为它的自然比较方法
 *
 * String当中的自然排序比较方法: 是按照字典排序进行比较的,处在字典后面的字符要大(字典可以看做Unicode编码集)
 *
 * Comparable接口自然排序的实现:
 *      1.需要类实现Comparable接口,重写compareTo方法(定义比较的规则)
 *      2.需要一个该对象的容器(数组和集合都可以)
 *      3.如果容器是数组,使用Arrays.sort(数组)进行排序,如果容器是集合,使用Collections.sort(集合工具类)
 *          排序的规则是:按照从小到大的顺序排列
 *          排序是在原先数组的基础上直接完成的,无需生成新数组
 * 字符串的自然排序在实际开发中具有一定的用途,直接使用Arrays.sort或者Collections.sort直接完成排序
 *
 * 使用Comparable接口完成自然排序有什么局限性?
 *      1.仅仅想要排序,却需要对源码进行修改,这在很多时候是做不到的
 *      2.虽然是功能接口,但实际上不能用lambda表达式
 *      3.类中除了compareTo方法比较对象大小,还有从Object当中继承过来的equals方法比较大小
 *          这两种比较大小的方法结果是不同冲突的,否则会带来一些麻烦
 *         建议这两个方法同true同0，同false非0，这样从逻辑上更顺畅
 *        比如一个有序集合，compareTo认为不相等，equals方法认为相等，可能导致集合添加元素失败
 *        实际上，所有实现 Comparable 的 Java 核心类都具有与 equals 一致的自然排序
 *        两个方法都用成员变量的取值重写即可
 *
 * 以上,使用Comparable接口完成自然排序,局限性很大,不是特别需要,不建议使用,如果仅仅是需要做一个排序的操作
 *      推荐使用Comparator接口完成自然排序
 *
 * Comparator接口是一个功能接口,其中必须要实现的抽象方法是compare,他就是表示对象的比较规则
 *      int compare(T o1, T o2);
 * 该方法的返回值是一个整数:
 *      如果返回值小于0,意味着 o1要小,o2要大
 *      如果返回值大于0,意味着 o1要大,o2要小
 *      如果返回值等于0,意味着 o1和o2相等
 *
 * Comparator接口自然排序的实现:
 *      1.需要一个对象容器(数组或者集合)
 *      2.使用Arrays.sort(数组,Comparator接口的实现类对象)或Collections.sort(集合,Comparator接口的实现类对象)方法完成自然排序
 *           在这里,Comparator接口的实现类对象称之为比较器,它传入的实际上是compare方法的比较规则
 *     3.该方法仍然是按照容器中元素从小到大进行自然排序
 *
 *自然在实际开发中的作用:
 * 在开发中,对数据进行筛选,然后完成排序,是天天都要写的功能,排序一般依赖以下两种方式:
 *      1.最常用的,是用数据库的SQL语句进行直接排序(80%)
 *      2.有些时候不方便使用SQL语句排序或者没必要,这个时候就可以使用自然排序(针对的是集合)
 *
 */
public class Demo2 {
    public static void main(String[] args) {
        /*//a,abc,abcd,bcd,bd,ccc
        String[] arr = {"abc", "a", "abcd", "bcd", "bd", "ccc"};
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));*/

       /* Student[] arr = {new Student(18, 100), new Student(20, 8), new Student(38, 0), new Student(1, 90), new Student(5, 40), new Student(88, 88), new Student(77, 7)};
        //Arrays.sort方法会自动把容器中任意两个元素进行比较,自动排序
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));*/


        Student[] arr = {new Student(10, 60, "三"), new Student(10, 60, "张三"), new Student(20, 60, "老王"), new Student(10, 70, "儿子的朋友"), new Student(20, 70, "妹妹"), new Student(20, 80, "张三的奶奶"), new Student(20, 80, "张三的二大爷的七大姑")};

        //1.朴素的方法
        //Arrays.sort(arr,new MyRuler());
        //System.out.println(Arrays.toString(arr));

        //2.高级一点的方法 匿名内部类
        //Arrays.sort(arr, new Comparator<Student>() {
        //    @Override
        //    public int compare(Student o1, Student o2) {
        //        //比较规则: 分数越高,越不行(分数越小,越排在前面)
        //        return ((int) ((o1.score - o2.score) * 100));
        //    }
        //});
        //System.out.println(Arrays.toString(arr));

        //3.高大上的方法,lambda表达式
        //借助方法完成lambda表达式的类型推断
        //规则: 按照name的长度从小到大排序
        //Arrays.sort(arr, (s1, s2) -> {
        //    return s1.name.length() - s2.name.length();
        //});
        ////简化:
        //Arrays.sort(arr, (s1, s2) -> s1.name.length() - s2.name.length());
        //System.out.println(Arrays.toString(arr));

        //方法引用
        Arrays.sort(arr,Demo2::myCompare);
        System.out.println(Arrays.toString(arr));

    }

    /**
     * 最终给出一个比较规则:
     * 先按照年龄进行排序(从小到大),如果年龄一致按照分数排序(从大到小)
     * 如果分数也一致,按照名字的长度进行排序(从大到小)
     *
     * 考虑使用lambda表达式的方法引用
     */

    public static int myCompare(Student s1, Student s2) {
        if (s1.age != s2.age) return s1.age - s2.age;
        if (Double.compare(s1.score, s2.score) != 0) return Double.compare(s2.score, s1.score);
        return s2.name.length() - s1.name.length();
    }
}

class MyRuler implements Comparator<Student> {


    @Override
    public int compare(Student o1, Student o2) {
        //按照年龄从小到大排序
        return o1.age - o2.age;
    }
}

class Student implements Comparable<Student> {
    int age;
    double score;
    String name;

    public Student(int age, double score, String name) {
        this.age = age;
        this.score = score;
        this.name = name;
    }

    public Student() {
    }

    public Student(int age, double score) {
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", score=" + score +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        //该方法表示自然排序的比较规则,返回0意味着相等,返回小于0的数意味着前面的小,返回大于0的数意味着前面的大
        //比较规则1: 谁考得高谁老大(排在前面,意味着它小,考得越好,越小)
        //我考的比你高,但是调用该方法我却更小,却要返回一个小于0的数
        //需要让你的分数减去我的分数,我考的越高,方法的返回值越是一个负数(越小)
        //double类型表示0.1有可能表示成0.099999
        //return ((int) ((o.score - this.score) * 100));

        //比较规则2: 谁的年龄小,谁就是老大(按照年龄从小到大排序)
        return this.age - o.age;
    }
}
